//: Playground - noun: a place where people can play

import UIKit

var str = "Hello, playground"

func addString(str1:String, str2:String) -> Int? {
    if let int1 = Int(str1) {
        if let int2 = Int(str2) {
            return int1 + int2
        }
        return int1
    }
    else {
        return Int(str2)
    }
}

print(addString("10", str2: "20"))
addString("10h", str2: "20h")

func addAllString(arr:[String]) -> Int? {
    var count = "0"
    var number = 0
    for str in arr {
        number = addString(count, str2: str)!
        count = String(number)
    }
    return number
}

var arr = ["20", "30", "40", "50h"]
print(addAllString(arr))

//2.
func findIndex (str: String, arr: [String]) -> Int? {
    var index = 0
    for indexStr in arr {
        if str == indexStr {
            return index
        }
        index += 1
    }
    return -1
}

func findIndex1(str: String, arr: [String]) -> Int? {
    var index:Int? = 0
    var indexNumber = 0
    for indexStr in arr {
        if str == indexStr {
            index = indexNumber
            break
        }
        indexNumber += 1
    }
    return index
}

//3.
var str1 = "30h"
var arr1 = ["30", "30h", "30h2", "30h4", "30h5"]
print(findIndex(str1, arr: arr1))
print(findIndex1(str1, arr: arr1))

//4.
func  showDebugString <T: Comparable> (arr: [T]) -> Void {
    for str in arr {
        print(str)
    }
}

showDebugString(arr1)

//5.
var myNumber:Float = 2
var myDivisionResult = 7 / myNumber

var arr2 = [10, 9, 8, 12, 14, 11, 10.3, 17, 1.4]
let arr3 = arr2.sort { (a, b) -> Bool in
    return a > b
}

print(arr2, arr3)

//6.
func stringCreator(initialString: String) -> (() -> String) {
    func printString () -> String {
        return initialString
    }
    return printString
}

var stringCreator1 = stringCreator("10h")
stringCreator1()


//7.
func showTheRegion (origin: (x : Double, y : Double)) -> (Double, Double, String) {
    var region = "第一象限"
    let originX = origin.x;
    let originY = origin.y;
    if originX >= 0 {
        if originY < 0 {
            region = "第四象限"
        }
    }
    else {
        if originY >= 0 {
            region = "第二象限"
        }
        else {
            region = "第三象限"
        }
    }
    return (originX, originY, region)
}

//9.
//func showTheRegion1 (origin: (x : Double, y : Double)) -> (Double, Double, String) {
//    
//}
func showTheRegion1 (origin: (x : Double, y : Double)) -> (Double, Double, String) {
    var region = ""
    let x = origin.x
    let y = origin.y
    switch (origin) {
    case (x, y) where x >= 0 && y >= 0:
        region = "第一象限"
    case (x, y) where x >= 0 && y < 0:
        region = "第四象限"
    case (x, y) where x < 0 && y >= 0:
        region = "第二象限"
    case (x, y) where x < 0 && y < 0:
        region = "第三象限"
    default:
        region = ""
    }
    return (x, y, region)
}

var origin1 = (10.7, -10.7)
var origin2 = (10.7, 10.7)
var origin3 = (-10.7, 10.7)
var origin4 = (-10.7, -10.7)
var arr4 = [origin1, origin2, origin3, origin4]
for origin in arr4 {
    print(showTheRegion(origin))
}

for origin in arr4 {
    print(showTheRegion1(origin))
}

//8.
func showTheTuple<T: Comparable> (originArr: [T]) -> [(T, T)] {
    var arr = [(T, T)]()
    var index = 0
    for _ in 0..<originArr.count / 2 {
        let tuple = (originArr[index], originArr[index + 1])
        index += 2
        arr.append(tuple)
    }
    return arr
}

var originArr = [10, 7, 8, 3, 4, 5, 6]
print(showTheTuple(originArr))

//10.返回卦限
func showTheOctant (tuple1:(x: Double, y: Double, z: Double)) -> String {
    var region = ""
    let x = tuple1.x
    let y = tuple1.y
    let z = tuple1.z
    switch (tuple1) {
    case (x, y, z) where z >= 0.0 :
        switch (x, y) {
        case (x, y) where x >= 0.0 && y >= 0.0 :
            region = "I"
        case (x, y) where x < 0.0 && y >= 0.0:
            region = "II"
        case (x, y) where x >= 0.0 && y < 0.0:
            region = "IV"
        default:
            region = "III"
        }
    default:
        switch (x, y) {
        case (x, y) where x >= 0.0 && y >= 0.0 :
            region = "V"
        case (x, y) where x < 0.0 && y >= 0.0:
            region = "VI"
        case (x, y) where x >= 0.0 && y < 0.0:
            region = "VIII"
        default:
            region = "VII"
        }
    }
    return region
}

var arr5 = [(10.1, 12.3, 1.3), (10.4, 12.4, -1.5), (10.4, -10.6, 10.7), (10.4, -10.6, -1.4), (-10.2, 1.4, 1.4), (-10.2, 1.4, -1.4), (-10.2, -1.4, 1.4), (-10.2, -14.4, -14.4)]
for origin in arr5 {
    print(showTheOctant(origin));
}

//字典，数组（值类型，还是引用类型传递)
//1.此种情况下，value为值类型时字典为值类型
var dict1 = [1 : 10, 3 : 20, 4 : 35]
var dict2 = dict1
var dict3 = dict1
var dict4 = dict1
//修改
dict1[1] = 33
//删除
dict3.removeValueForKey(1)
//增加
dict4[5] = 44

print("\(dict1),\n \(dict2),\n \(dict3),\n \(dict4)")
//2.


//数组(值类型）
var array01 = [1, 2, 3, 4, 5, 6, 7]
var array02 = array01
var array03 = array01
var array04 = array01
var array05 = array01
var array06 = array01
//增加(值类型）
array02.append(8)
//删除(值类型）
array03.removeLast()
//修改(值类型）
array01[0] = 10


print("\(array01)\n, \(array02)\n, \(array03)\n, \(array04)\n, \(array05)\n")

//字典第二种情况(值类型）
var arrayDict1 = [1 : array01]
var arrayDict2 = arrayDict1
var arrayDict3 = arrayDict1
var arrayDict4 = arrayDict1
//修改（值类型）
arrayDict1[1] = array02
//增加
arrayDict2[2] = [1, 4, 5]
//删除
arrayDict3.removeValueForKey(1)


print("\(arrayDict1)\n, \(arrayDict2)\n, \(arrayDict3)\n, \(arrayDict4)\n")
//综上，字典和数组在swift2.2中均是值类型，也就不是引用类型

//结构体setter
struct Point {
    var x = 0.0
    var y = 0.0
}

struct Size {
    var width = 0.0
    var height = 0.0
}

struct AlertnativeRect {
    var origin = Point()
    var size = Size()
    var center : Point {
        get {
            let centerX = origin.x + size.width / 2.0
            let centerY = origin.y + size.height / 2.0
            return Point(x: centerX, y: centerY)
        }
        set {
            origin.x = newValue.x - size.width / 2.0
            origin.y = newValue.y - size.height / 2.0
        }
    }
}

let point1 = Point(x: 1.0, y: 2.5)
let size1 = Size(width: 10, height: 4.3)
let rect1 = AlertnativeRect(origin: point1, size: size1)
rect1.center

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume : Double {
        return width * height * depth
    }
}

let cuboid01 = Cuboid(width: 1.345, height: 2.345, depth: 13.2345)
cuboid01.volume

//willSet/didSet
class StepCounter {
    //    这种方式是错误的，编译器不知道类型，get/set方法无法匹配,况且已经有设定值
    //    var totalSteps = 0 {
    //        didset {
    //            
    //        }
    //    }
    
    //正确方式
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {    //willSet会将newValue赋值
            print("About to set totalStepts to \(newTotalSteps)")
        }
        didSet {    //didSet会保留之前的数据
            if totalSteps > oldValue {
                print("Add \(totalSteps - oldValue) steps")
            }
        }
    }
    
}

let stepCounter01 = StepCounter()
stepCounter01.totalSteps = 10
stepCounter01.totalSteps = 30

/***
 实例的属性属于一个特定类型实例，每次类型实例化后都用拥有自己的一套属性值，实例之间的属性相互独立
 也可以为实例本身定义属性，不管属性有多少个实例，这些属性都只有唯一一份。这种属性就是类型属性（OC类方法,属性)
 类型属性用于定义特定类型所有实例共享的数据，比如所有实例都能用用的一个常量（就像C语言中的静态常量），或者所有实例都能访问的一个变量（就像C语言中的静态变量）。
 对于值类型（指结构体和枚举）可以定义存储型和计算型类型属性，对于类（class）则只能定义计算型类型属性。
 值类型的存储型类型属性可以使变量或常量（无get/set)，计算型类型属性(get/set)跟实例的计算属性一样定义成变量属性
 注意：跟实例的存储属性不同，必须给存储型类型属性指定默认值，因为类型本身无法在初始化过程中使用构造器给类型属性赋值
 类型属性：类型属性用于定义特定类型所有实例共享的数据
 
 
 类型属性语法
 在C或Objective-C中，静态常量和静态变量的定义是通过特定类型加上global关键字。在swift编程语言中，类型属性是作为类型定义的一部分写在类型最外层的花括号内，因此它的作用范围也就在类型支持的范围内
 
 使用关键字static来定义值类型的类型属性，关键字class来为类(class)定义类型属性。
 static var 类型属性变量，可以修改
 ***/
struct SomeStructure {
    static var storedTypeProperty = "Some Structure value"
    static var computedTypeProperty: Int {
        return 10
    }
}

enum  SomeEnumeration {
    static var storedTypeProperty = "Some Enumeration Value."
    static var computedTypeProperty: Int {
        return 20
    }
}

class SomeClass {
    //但是在class中使用static也能设置存储属性，不过最好在类中还是使用class关键字为好
    static var storedTypeProperty = "Some Class"
    //这里不能出现存储属性，也就和OC一样，表示类方法
    //再次表明swift是在OC基础上发展的。OC没有类变量这一说法
    //类型属性
    class var computedTypeProperty: Int {
        return 30
    }
}

var someStructure01 = SomeStructure.computedTypeProperty
var someEnumeration01 = SomeEnumeration.computedTypeProperty
var someClass01 = SomeClass.computedTypeProperty
var someClassStoredProperty = SomeClass.storedTypeProperty
SomeStructure.storedTypeProperty = "Should be Change"
print("\(SomeStructure.storedTypeProperty)")

SomeEnumeration.storedTypeProperty = "Change Enumeration"
print("\(SomeEnumeration.storedTypeProperty)")

SomeClass.storedTypeProperty = "Changed Class"
print("\(SomeClass.storedTypeProperty)")

var someClass02 = SomeClass()

//static在struct,enum中作为类型属性，类型方法的关键字
struct Point01 {
    let x: Double
    let y: Double
    // 存储属性
    static let zero = Point01(x: 0, y: 0)
    // 计算属性
    static var ones: [Point01] {
        return [Point01(x: 1, y: 1),
                Point01(x: -1, y: 1),
                Point01(x: 1, y: -1),
                Point01(x: -1, y: -1)]
    }
    // 类型方法
    static func add(p1: Point01, p2: Point01) -> Point01 {
        return Point01(x: p1.x + p2.x, y: p1.y + p2.y)
    }
}

struct AudioChannel {
    static let thresholdLevel = 10
    static var maxInputLevelForAllChannels = 0
    var currentLevel:   Int = 0 {
        didSet {
            if currentLevel > AudioChannel.thresholdLevel {
                //将新电平值设置成阈值
                currentLevel = AudioChannel.thresholdLevel
            }
            if currentLevel > AudioChannel.maxInputLevelForAllChannels {
                //存储当前电平值为新的最大输入电平值
                AudioChannel.maxInputLevelForAllChannels = currentLevel
            }
        }
    }
}

var audioChannel01 = AudioChannel(currentLevel: 8)
audioChannel01.currentLevel = 7

/***
 方法是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法；实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法；类型方法与类型本身相关联。类型方法与Objective-C的类方法(class methods)相似
 
 结构体和枚举能够定义方法是swift与C/Objective-C的主要区别之一。在Objective-C中，类是唯一能定义方法的类型。但在swift中，你不仅能选择是否要定义一个类/结构体/枚举,还能灵活的在你创建的类型(类/结构体/枚举)上定义方法
 ***/

//游戏等级
//结构体写游戏等级
struct LevelTracker {
    static var highestUnlockedLevel = 6
    static func unlockLevel (level: Int) {
        if level > highestUnlockedLevel {
            highestUnlockedLevel = level
        }
    }
    
    static func levelIsUnlocked(level: Int) -> Bool {
        return level <= highestUnlockedLevel
    }
    
    var currentLevel = 1
    //使用mutating是因为方法内部更改了结构体的成员变量
    mutating func advanceToLevel(level: Int) -> Bool {
        if LevelTracker.levelIsUnlocked(level) {
            currentLevel = level
            return true
        }
        else {
            return false
        }
    }
}

var level01 = LevelTracker()
level01.currentLevel = 2
LevelTracker.highestUnlockedLevel
LevelTracker.unlockLevel(level01.currentLevel)
LevelTracker.highestUnlockedLevel

//类放游戏玩家，每个玩家都有游戏等级结构体
class Player {
    var tracker = LevelTracker()
    let playerName: String
    func completedLevel(level: Int) {
        LevelTracker.unlockLevel(level + 1)
        tracker.advanceToLevel(level)
    }
    init(name: String) {
        playerName = name
    }
    func description() -> String {
        return ("\(playerName), levele = \(tracker.currentLevel)")
    }
}

var player01 = Player(name: "zmj")
var player02 = Player(name: "wjl")
var player03 = Player(name: "baby01")
var player04 = Player(name: "baby02")
var playerArray01 = [player01, player02, player03, player04]

func playerContinueGame(arr: [Player])  {
    for playerI in arr {
        let level = random() % 4
        playerI.completedLevel(level)
        print("\(playerI.description())")
    }
}

/****
 //延迟处理在playground中是不能使用，GCD还是要在项目中使用为好
 var time = dispatch_time(DISPATCH_TIME_NOW, Int64(1.5 * Double(NSEC_PER_SEC)))
 let queue_t = dispatch_get_main_queue()
 
 func afterPlayerGame () {
 print("延迟执行")
 dispatch_after(time, queue_t) {
 playerContinueGame(playerArray01)
 print("2秒后执行")
 }
 }
 
 afterPlayerGame()
 //afterPlayerGame()
 ****/

playerContinueGame(playerArray01)
playerContinueGame(playerArray01)

//类，继承，重载，重写
//基类
class Vehicle {
    var numberOfWheels: Int
    var maxPassengers:  Int
    func description() -> String {
        return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers"
    }
    init() {
        numberOfWheels = 0
        maxPassengers = 1
    }
}
var vehicle01 = Vehicle()

class Bicycle: Vehicle {
    override init() {
        super.init()
        numberOfWheels = 2
    }
}
var bicycle01 = Bicycle()

class Tandem: Bicycle {
    override init() {
        super.init()
        maxPassengers = 2
    }
}
var tandem01 = Tandem()

//重写方法
class Car: Vehicle {
    var speed: Double = 0.0
    override init() {
        super.init()
        maxPassengers = 5
        numberOfWheels = 4
    }
    override func description() -> String {
        return super.description() + ";\n" + "traveling at \(speed) mph"
    }
}
var car01 = Car()
print(car01.description())

//重写属性
class SpeedLimitedCar: Car {
    override var speed: Double {
        get {
            return super.speed
        }
        set {
            super.speed = min(newValue, 40.0)
        }
    }
}
var speedLimitedCar01 = SpeedLimitedCar()
speedLimitedCar01.speed = 50.0
print(speedLimitedCar01.description())

class AutomaticCar: Car {
    var gear = 1
    override var speed: Double {
        didSet {
            gear = Int(speed / 10.0) + 1
        }
    }
    override func description() -> String {
        return super.description() + " in gear \(gear)"
    }
}
var automaticCar01 = AutomaticCar()
print(automaticCar01.description())
automaticCar01.speed = 60.0
print(automaticCar01.description())

//构造参数
struct Celsius {
    var temperatureInCelsius: Double = 0.0
    init (fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointOfWater = Celsius(fromKelvin: 273.15)

//可选属性类型
/***
 如果你定制的类型包含一个逻辑上允许取值为空的存储型属性--不管是因为它无法在初始化时赋值，还是因为它可以在之后某个时间点可以赋值为空--你都需要将它定义为可选类型optionalType。可选类型的属性将自动初始化为空nil。表示这个属性是故意在初始化时设置为空的。
 ***/
class SurveyQuestion {
    var text: String
    var response: String?
    init (text: String) {
        self.text = text
    }
    func ask()  {
        if let responsed = response {
            print("\(text), response = \(responsed)")
        }
        else {
            print("\(text), no response")
        }
    }
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
cheeseQuestion.response = "NO, I Don\'t like it"
cheeseQuestion.ask()

/***
 //构造器链
 为了简化指定构造器和遍历构造器之间的调用关系，swift采用以下三条规则来限制构造器之间的代理调用:
 规则1
 指定构造器必须调用其直接父类的指定构造器
 规则2
 便利构造器必须调用同一类中定义的其它构造器
 规则3
 便利构造器必须最终以调用一个指定构造器结束
 
 一个更方便记忆的方法是：
 指定构造器必须总是向上代理
 便利构造器必须总是横向代理
 ***/

/***
 swift编译器将执行4中有效的安全检查，以确保两段式构造过程能顺利完成：
 安全检查1
 指定构造器必须保证它所在类引入的所有属性都必须先初始化完成，之后才能将其他构造任务向上代理给父类中的构造器
 安全检查2
 指定构造器必须先向上代理调用父类构造器，然后再为继承的属性设置新值。如果没这么做，指定构造器赋予的新值将被父类中的构造器所覆盖
 安全检查3
 便利构造器必须先代理调用同一类中的其他构造器，然后再为任意属性赋新值。如果没这么做，便利构造器赋予的新值将被同一类中其他指定构造器所覆盖
 安全检查4
 构造器在第一阶段构造完成之前，不能调用任何实例方法，不能读取任何实例属性的值，也不能引用self的值
 ***/

/***
 假如要为子类中引入的任意新属性提供默认值，请遵守一下2个规则：
 规则1
 如果自雷没有定义任何指定构造器，它将自动继承所有父类的指定构造器
 规则2
 如果自雷提供了所有父类指定构造器的实现--不管是通过规则1继承过来的，还是通过自定义实现的--它将自动继承所有父类的便利构造器
 ***/

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}
let namedMeat = Food(name: "Bacon")
let nameUn = Food()

class RecipeIngredient: Food {
    var quantity: Int
    init (name:String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    override convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
}

let recipeIngredient01 = RecipeIngredient(name: "Monkey")

class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var description: String {
        let output = "\(quantity) x \(name.lowercaseString)"
        return output
    }
}

let shoppingListItem01 = ShoppingListItem()
var breakfastList = [
    ShoppingListItem(),
    ShoppingListItem(name: "Bacon"),
    ShoppingListItem(name: "Eggs", quantity: 6),]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true

for item in breakfastList {
    print(item.description)
}

struct Checkerboard {
    let boardColors: [Bool] = {
        var temporaryBoard = [Bool]()
        var isBlack = false
        for i in 1...10 {
            for j in 1...10 {
                temporaryBoard.append(isBlack)
                isBlack = !isBlack
            }
            isBlack = !isBlack
        }
        return temporaryBoard
    }()
    
    func squareIsBlackAtRow(Row row: Int, column: Int) -> Bool? {
        if (row * column + column >= boardColors.count) {
            return nil
        }
        return boardColors[(row * column) + column]
    }
}

var checkBoard01 = Checkerboard()
checkBoard01.boardColors

checkBoard01.squareIsBlackAtRow(Row: 10, column: 9)

/***
 //2.15.反初始化
 在一个类的实例被释放之前，反初始化函数被立即调用。用关键字deinit来标示反初始化函数，类似于初始化函数用init来标示。反初始化函数只适用于类类型
 反初始化原理：
 swift会自动释放不再需要的实例以释放资源。
 deinit {
 //执行反初始化
 }
 反初始化函数是在实例释放发生前一步被自动调用。不允许主动调用自己的反初始化函数。子类继承了父类的反初始化函数，并且在子类反初始化函数实现的最后，父类的反初始化函数被自动调用。即使子类没有提供自己的反初始化函数，父类的反初始化函数也总是被调用。
 ***/
//show the code
struct Bank {
    static var coinsInBank = 10_000
    static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
        numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
        coinsInBank -= numberOfCoinsToVend
        return numberOfCoinsToVend
    }
    
    static func receiveCoins (coins: Int) {
        coinsInBank += coins
    }
}

class BankPlayer {
    var coinsInPurse: Int
    init(coins: Int) {
        coinsInPurse = Bank.vendCoins(coins)
    }
    
    func winCoins(coins: Int)  {
        coinsInPurse += Bank.vendCoins(coins)
    }
    
    deinit {
        Bank.receiveCoins(coinsInPurse)
    }
}
var bankPlayer01 = BankPlayer(coins: 10)
bankPlayer01.winCoins(30)

var bankPlayer02: BankPlayer? = BankPlayer(coins: 40)
bankPlayer02!.winCoins(60)
bankPlayer02!.winCoins(2_000)
Bank.coinsInBank
bankPlayer02 = nil
Bank.coinsInBank

//swift提供两种方法来解决强引用环：弱引用和无主引用（和OC中一样，weak,unsafed owned)
//弱引用和无主引用允许引用环中的一个实例引用另外一个实例，但不是强引用。因此实例可以互相引用但是不会产生强引用环
//对于生命周期中引用会变为nil的实例，使用弱引用；对于初始化时赋值之后引用再也不会赋值为nil的实例，使用无主引用
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinit")}
}

class Apartment {
    let num: Int
    init(num: Int) { self.num = num }
    weak var person: Person?
    deinit { print("\(person)'s apartment deinit, num = \(num)") }
}

var person01: Person? = Person(name: "zmj")
var apartment01: Apartment? = Apartment(num: 26)
person01!.apartment = apartment01
apartment01!.person = person01

person01 = nil
apartment01 = nil

//无主引用
/***
 和弱引用相似，无主引用也不强持有实例。但是和弱引用不同的是，无主引用默认始终幼稚。因此，无主引用只能定义为非可选类型。在属性，变量前添加
 unowned关键，可以声明一个无主引用
 
 因为是非可选类型，因此当使用无主引用的时候，不需要展开，可以直接访问。不过非可选类型变量不能赋值为nil，因此当实例被销毁的时候，ARC无法将引用赋值为nil
 **
 当实例被销毁后，试图访问该实例的无主引用会出发运行时错误。使用无主引用时请确保引用始终指向一个未销毁的实例。上面的非法操作100%应用崩溃，不会发生无法预期的行为。因此，你应该避免这种情况
 ***/

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
    deinit { print("Customer.name = \(name) deinit") }
}

class CreditCard {
    let number: Int
    //无主引用，常量
    unowned let customer: Customer
    init(number: Int, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit{ print("CreditCard.number = \(number) deinit") }
}
var customer01: Customer? = Customer(name: "wjl")
var card01: CreditCard? = CreditCard(number: 28, customer: customer01!)
var customer02: Customer? = Customer(name: "xiaoming")
customer02!.card = CreditCard(number: 19, customer: customer02!)
customer01 = nil
card01 = nil
customer02 = nil

